﻿#if DEBUG
#define ThrowNull
#endif
using System;
using System.ComponentModel;
using System.Collections.Generic;
using System.Diagnostics;

namespace System {
	/// <summary>
	/// Main Unit
	/// </summary>
	#if !SILVERLIGHT
	[Serializable()]
	#endif
	public class PackageUnit : PackageBase, INotifyPropertyChanged {
		#region Fields
		public static readonly PackageProperty IsInEditProperty = PackageProperty.Register("IsInEdit", typeof(Boolean), typeof(PackageUnit));


#if !SILVERLIGHT
		[NonSerialized()]
#endif
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		private PackageUnit _backup;
		#if !SILVERLIGHT
		[NonSerialized()]
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
#endif
		private PropertyChangedEventHandler _propertyChanged;
		#endregion
		#region Protected
		/// <summary>
		/// Called when Value changes.
		/// </summary>
		/// <param name="property">The property.</param>
		/// <param name="value">The value.</param>
		protected override void OnValueChanged(PackageProperty property, object value) {
			base.OnValueChanged(property, value);
			this.OnPropertyChanged(property);
		}
		/// <summary>
		/// Called when [property changed].
		/// </summary>
		/// <param name="property">The property.</param>
		protected virtual void OnPropertyChanged(PackageProperty property) {
			if (property == null) {
				throw new ArgumentNullException("property");
			}
			this.OnPropertyChanged(property.Name);
		}
		/// <summary>
		/// Called when [property changed].
		/// </summary>
		/// <param name="property">The property.</param>
		protected virtual void OnPropertyChanged(String property) {
			if (_propertyChanged != null) {
				_propertyChanged(this, new PropertyChangedEventArgs(property));
			}
		}
		#endregion
		#region Public
		/// <summary>
		/// Begins the edit only of this object.
		/// Use static functions to enumerate though children.
		/// </summary>
		public virtual void BeginEdit() {
			if (!this.IsInEdit) {
				_backup = this.Clone() as PackageUnit;
				this.IsInEdit = true;
			}
		}
		/// <summary>
		/// Ends the edit only of this object.
		/// Use static functions to enumerate though children.
		/// </summary>
		public virtual void EndEdit() {
			if (this.IsInEdit) {
				_backup = null;
				this.IsInEdit = false;
			}
		}
		/// <summary>
		/// Cancels the edit only of this object.
		/// Use static functions to enumerate though children.
		/// </summary>
		public virtual void CancelEdit() {
			if (this.IsInEdit) {
				PackageBase.CopyBothValues(_backup, this);
				_backup = null;
				this.IsInEdit = false;
			}
		}
		#endregion
		#region Static
		/// <summary>
		/// Begins the edit of the object and all child objects.
		/// </summary>
		/// <param name="source">The source.</param>
		public static void BeginEdit(PackageUnit source) {
#if ThrowNull
			if (source == null) {
				throw new ArgumentNullException("source");
			}
#endif
			Type local = typeof(PackageUnit);
			source.BeginEdit();
			foreach (KeyValuePair<PackageProperty, Object> pair in source) {
				if (pair.Key.PropertyType == local || pair.Key.PropertyType.IsSubclassOf(local)) {
					(pair.Value as PackageUnit).BeginEdit();
				}
			}

		}
		/// <summary>
		/// Ends the edit of the object and all child objects.
		/// </summary>
		/// <param name="source">The source.</param>
		public static void EndEdit(PackageUnit source) {
#if ThrowNull
			if (source == null) {
				throw new ArgumentNullException("source");
			}
#endif
			Type local = typeof(PackageUnit);
			source.EndEdit();
			foreach (KeyValuePair<PackageProperty, Object> pair in source) {
				if (pair.Key.PropertyType == local || pair.Key.PropertyType.IsSubclassOf(local)) {
					(pair.Value as PackageUnit).EndEdit();
				}
			}

		}
		/// <summary>
		/// Cancels the edit of the object and all child objects.
		/// </summary>
		/// <param name="source">The source.</param>
		public static void CancelEdit(PackageUnit source) {
#if ThrowNull
			if (source == null) {
				throw new ArgumentNullException("source");
			}
#endif
			Type local = typeof(PackageUnit);
			source.CancelEdit();
			foreach (KeyValuePair<PackageProperty, Object> pair in source) {
				if (pair.Key.PropertyType == local || pair.Key.PropertyType.IsSubclassOf(local)) {
					(pair.Value as PackageUnit).CancelEdit();
				}
			}
		}
		#endregion
		#region Property
		/// <summary>
		/// Gets a value indicating whether this package is in edit.
		/// </summary>
		/// <value>
		/// 	<c>true</c> if this package is in edit; otherwise, <c>false</c>.
		/// </value>
		public virtual Boolean IsInEdit {
			get {
				return (Boolean)this.GetValue(IsInEditProperty);
			}
			protected set {
				this.SetValue(IsInEditProperty, value);
				if (!value) {
					_backup = null;
				}
			}
		}
		#endregion
		#region Events
		/// <summary>
		/// Occurs when a property value changes.
		/// </summary>
		public event PropertyChangedEventHandler PropertyChanged {
			add {
				_propertyChanged = PropertyChangedEventHandler.Combine(_propertyChanged, value) as PropertyChangedEventHandler;
			}
			remove {
				_propertyChanged = PropertyChangedEventHandler.Remove(_propertyChanged, value) as PropertyChangedEventHandler;
			}
		}
		#endregion
	}
}
